//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using OpenLS.UI;

namespace OpenLS.Spreadsheet.UI
{
    public sealed class WorksheetGrid : Panel
    {
        private const FrameworkPropertyMetadataOptions op =
            FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsArrange;

        public static readonly DependencyProperty ColumnsProperty =
            DependencyProperty.Register("Columns", typeof (ColumnCollection), typeof (WorksheetGrid),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsArrange,
                                                                      OnColumnsChanged));

        public static readonly DependencyProperty LeftProperty =
            DependencyProperty.Register("Left", typeof (int), typeof (WorksheetGrid),
                                        new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange));

        public static readonly DependencyProperty RowsProperty =
            DependencyProperty.Register("Rows", typeof (RowCollection), typeof (WorksheetGrid),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsArrange,
                                                                      OnRowsChanged));

        public static readonly DependencyProperty TopProperty =
            DependencyProperty.Register("Top", typeof (int), typeof (WorksheetGrid),
                                        new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange,
                                                                      onTopChanged));

        public static DependencyProperty ColumnOffsetProperty = DependencyProperty.RegisterAttached("ColumnOffset",
                                                                                                    typeof (double),
                                                                                                    typeof (
                                                                                                        WorksheetGrid),
                                                                                                    new FrameworkPropertyMetadata
                                                                                                        (0.0, op));

        //\\const FrameworkPropertyMetadataOptions op2 = FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure;

        public static DependencyProperty ColumnProperty = DependencyProperty.RegisterAttached("Column", typeof (int),
                                                                                              typeof (WorksheetGrid),
                                                                                              new FrameworkPropertyMetadata
                                                                                                  (0,
                                                                                                   FrameworkPropertyMetadataOptions
                                                                                                       .
                                                                                                       AffectsParentArrange,
                                                                                                   OnCellAttachedPropertyChanged),
                                                                                              IsIntValueNotNegative);

        public static DependencyProperty ColumnSpanProperty = DependencyProperty.RegisterAttached("ColumnSpan",
                                                                                                  typeof (int),
                                                                                                  typeof (WorksheetGrid),
                                                                                                  new FrameworkPropertyMetadata
                                                                                                      (1,
                                                                                                       new PropertyChangedCallback
                                                                                                           (OnCellAttachedPropertyChanged)),
                                                                                                  IsIntValueGreaterThanZero);

        public static DependencyProperty IsFullColumnProperty = DependencyProperty.RegisterAttached("IsFullColumn",
                                                                                                    typeof (bool),
                                                                                                    typeof (
                                                                                                        WorksheetGrid),
                                                                                                    new PropertyMetadata
                                                                                                        (false));

        public static DependencyProperty IsFullRowProperty = DependencyProperty.RegisterAttached("IsFullRow",
                                                                                                 typeof (bool),
                                                                                                 typeof (WorksheetGrid),
                                                                                                 new PropertyMetadata(
                                                                                                     false));

        public static DependencyProperty ItemHeightProperty = DependencyProperty.RegisterAttached("ItemHeight",
                                                                                                  typeof (double),
                                                                                                  typeof (WorksheetGrid),
                                                                                                  new FrameworkPropertyMetadata
                                                                                                      (double.NaN, op, null,
                                                                                                       ForcePositive));

        public static DependencyProperty ItemWidthProperty = DependencyProperty.RegisterAttached("ItemWidth",
                                                                                                 typeof (double),
                                                                                                 typeof (WorksheetGrid),
                                                                                                 new FrameworkPropertyMetadata
                                                                                                     (double.NaN, op, null,
                                                                                                      ForcePositive));

        public static DependencyProperty OverflowProperty = DependencyProperty.RegisterAttached("Overflow",
                                                                                                typeof (bool),
                                                                                                typeof (WorksheetGrid),
                                                                                                new FrameworkPropertyMetadata
                                                                                                    (false, op));

        public static DependencyProperty RowOffsetProperty = DependencyProperty.RegisterAttached("RowOffset",
                                                                                                 typeof (double),
                                                                                                 typeof (WorksheetGrid),
                                                                                                 new FrameworkPropertyMetadata
                                                                                                     (0.0, op));

        public static DependencyProperty RowProperty = DependencyProperty.RegisterAttached("Row", typeof (int),
                                                                                           typeof (WorksheetGrid),
                                                                                           new FrameworkPropertyMetadata
                                                                                               (0,
                                                                                                new PropertyChangedCallback
                                                                                                    (OnCellAttachedPropertyChanged)),
                                                                                           IsIntValueNotNegative);

        public static DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached("RowSpan", typeof (int),
                                                                                               typeof (WorksheetGrid),
                                                                                               new FrameworkPropertyMetadata
                                                                                                   (1,
                                                                                                    new PropertyChangedCallback
                                                                                                        (OnCellAttachedPropertyChanged)),
                                                                                               IsIntValueGreaterThanZero);

        public static DependencyProperty VerticalOverflowProperty =
            DependencyProperty.RegisterAttached("VerticalOverflow", typeof (bool), typeof (WorksheetGrid),
                                                new FrameworkPropertyMetadata(false, op));

        private LayoutInfo layoutInfo;
        private Dictionary<int, List<int>> positions;

        public ColumnCollection Columns
        {
            get { return (ColumnCollection) GetValue(ColumnsProperty); }
            set { SetValue(ColumnsProperty, value); }
        }

        public RowCollection Rows
        {
            get { return (RowCollection) GetValue(RowsProperty); }
            set { SetValue(RowsProperty, value); }
        }

        public int Top
        {
            get { return (int) GetValue(TopProperty); }
            set { SetValue(TopProperty, value); }
        }

        public int Left
        {
            get { return (int) GetValue(LeftProperty); }
            set { SetValue(LeftProperty, value); }
        }

        internal WorksheetGrid GetHitGrid(Func<FrameworkElement, Point> fn)
        {
            Contract.Ensures(Contract.Result<WorksheetGrid>() != null);
            if (IsInside(this, fn))
                return this;
            FrameworkElement element = this;
            while (element != null && !IsInside(element, fn))
                element = (FrameworkElement) VisualTreeHelper.GetParent(element);
            if (element == null)
                return this;
            HitTestResult hit = VisualTreeHelper.HitTest(element, fn(element));
            if (hit == null)
                return this;
            var visual = hit.VisualHit as Visual;
            if (visual == null)
            {
                // Contract.Assert(false);
                return this;
            }
            var r = WindowServices.GetAncestor<WorksheetGrid>(visual);
            if (r != null)
                return r;
            return this;
        }

        private static bool IsInside(FrameworkElement source, Func<FrameworkElement, Point> mouseEventArgs)
        {
            Contract.Assert(source != null);
            Point pt = mouseEventArgs(source);
            return pt.X >= 0 && pt.Y >= 0 && pt.X <= source.ActualWidth && pt.Y <= source.ActualHeight;
        }

        private static void onTopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        private static object ForcePositive(DependencyObject d, object v)
        {
            if (v is double)
                return Math.Max((double) v, 0.0);
            return v;
        }

        private static void OnCellAttachedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as UIElement;
            if (element != null)
            {
                UIElement grid = VisualTreeHelper.GetParent(element) as WorksheetGrid;
                if (grid != null)
                {
                    grid.InvalidateArrange();
                    //\\grid.ArrangeChild(element, new Size(grid.ActualWidth, grid.ActualHeight), false);
                }
            }
        }

        private static bool IsIntValueGreaterThanZero(object value)
        {
            return (((int) value) > 0);
        }

        private static bool IsIntValueNotNegative(object value)
        {
            return (((int) value) >= 0);
        }


        [AttachedPropertyBrowsableForChildren]
        public static bool GetOverflow(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (bool) element.GetValue(OverflowProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static bool GetVerticalOverflow(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (bool) element.GetValue(VerticalOverflowProperty);
        }


        [AttachedPropertyBrowsableForChildren]
        public static int GetColumn(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (int) element.GetValue(ColumnProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static double GetItemWidth(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (double) element.GetValue(ItemWidthProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static double GetItemHeight(UIElement element)
        {
            if (element == null)

                throw new ArgumentNullException("element");
            return (double) element.GetValue(ItemHeightProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static double GetRowOffset(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (double) element.GetValue(RowOffsetProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static double GetColumnOffset(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (double) element.GetValue(ColumnOffsetProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static int GetColumnSpan(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (int) element.GetValue(ColumnSpanProperty);
        }


        [AttachedPropertyBrowsableForChildren]
        public static int GetRow(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (int) element.GetValue(RowProperty);
        }


        [AttachedPropertyBrowsableForChildren]
        public static int GetRowSpan(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (int) element.GetValue(RowSpanProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static bool GetIsFullColumn(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (bool) element.GetValue(IsFullColumnProperty);
        }

        [AttachedPropertyBrowsableForChildren]
        public static bool GetIsFullRow(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            return (bool) element.GetValue(IsFullRowProperty);
        }


        public static void SetColumn(UIElement element, int value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ColumnProperty, value);
        }

        public static void SetItemWidth(UIElement element, double value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ItemWidthProperty, value);
        }

        public static void SetItemHeight(UIElement element, double value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ItemHeightProperty, value);
        }

        public static void SetColumnOffset(UIElement element, double value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ColumnOffsetProperty, value);
        }

        public static void SetRowOffset(UIElement element, double value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(RowOffsetProperty, value);
        }

        public static void SetIsFullColumn(UIElement element, bool value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(IsFullColumnProperty, value);
        }

        public static void SetIsFullRow(UIElement element, bool value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(IsFullRowProperty, value);
        }

        public static void SetOverflow(UIElement element, bool value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(OverflowProperty, value);
        }

        public static void SetVerticalOverflow(UIElement element, bool value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(VerticalOverflowProperty, value);
        }


        public static void SetColumnSpan(UIElement element, int value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(ColumnSpanProperty, value);
        }


        public static void SetRow(UIElement element, int value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(RowProperty, value);
        }


        public static void SetRowSpan(UIElement element, int value)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            element.SetValue(RowSpanProperty, value);
        }


        private static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var This = (WorksheetGrid) d;
            var columns = (ColumnCollection) e.NewValue;
            if (columns != null)
                columns.LayoutChanged += This.OnLayoutChanged;
        }

        private void OnLayoutChanged(object sender, EventArgs args)
        {
            InvalidateMeasure();
            InvalidateArrange();
        }

        private static void OnRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var This = (WorksheetGrid) d;
            var rows = (RowCollection) e.NewValue;
            if (rows != null)
                rows.LayoutChanged += This.OnLayoutChanged;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            if (Columns == null)
                return finalSize;
            if (Rows == null)
                return finalSize;
            layoutInfo = new LayoutInfo(this, Columns, Rows, finalSize);
            foreach (UIElement element in InternalChildren)
            {
                if (GetOverflow(element))
                {
                    CalculatePositions();
                    break;
                }
            }
            bool clipToBounds = ClipToBounds;
            foreach (UIElement element in InternalChildren)
            {
                if (!(element is FrameworkElement))
                    continue;
                ArrangeChild((FrameworkElement) element, finalSize, clipToBounds);
            }
            return finalSize;
        }

        internal void CalculatePositions()
        {
            positions = new Dictionary<int, List<int>>();
            bool overflow = false;
            foreach (UIElement element in InternalChildren)
            {
                if (GetOverflow(element))
                {
                    overflow = true;
                    break;
                }
            }
            if (!overflow)
                return;
            foreach (UIElement element in InternalChildren)
            {
                if (element.Visibility != Visibility.Visible)
                    continue;
                int r = GetRow(element);
                int rowSpan = GetRowSpan(element);
                int column = GetColumn(element);
                if (GetIsFullColumn(element))
                    continue;
                for (int i = r; i < r + rowSpan; i++) //\\ pb rowSpan pb max;
                {
                    List<int> l;
                    if (!positions.TryGetValue(i, out l))
                    {
                        l = new List<int>();
                        positions.Add(i, l);
                    }

                    l.Add(column);
                }
            }
        }

        private Rect GetRect(UIElement element, Size size)
        {
            //\\problemisfull
            double w = GetItemWidth(element);
            if (double.IsNaN(w))
            {
                if (GetIsFullRow(element))
                    w = size.Width;
                else
                {
                    int columnSpan = GetColumnSpan(element);
                    Column col = Columns[GetColumn(element) + GetColumnSpan(element) - 1];
                    w = columnSpan == 1
                            ? col.ActualWidth
                            : col.Offset + col.ActualWidth - Columns[GetColumn(element)].Offset;
                }
            }

            double h = GetItemHeight(element);
            if (double.IsNaN(h))
            {
                if (GetIsFullColumn(element))
                    h = size.Height;
                else
                {
                    int rowSpan = GetRowSpan(element);
                    Row row = Rows[GetRow(element) + rowSpan - 1];
                    h = rowSpan == 1
                            ? row.ActualHeight
                            : row.Offset + row.ActualHeight -
                              Rows[GetRow(element)].Offset;
                }
            }
            var result = new Rect(new Point(Columns.GetOffset(GetColumn(element)) - Columns[Left].Offset,
                                            Rows.GetOffset(GetRow(element)) - Rows.GetOffset(Top)), new Size(w, h));
            return result;
        }

        public void ArrangeChild(FrameworkElement element, Size size, bool clipToBounds)
        {
            Rect r;
            if (GetOverflow(element))
            {
                //\\element.Measure(new Size(10000, 10000)); //\\
                int row = GetRow(element);
                int column = GetColumn(element);
                int rowSpan = GetRowSpan(element);
                int columnSpan = GetColumnSpan(element);
                r = GetRect(new CellAddress(row, column), columnSpan, rowSpan, element.DesiredSize);
            }
            else
            {
                int row = GetRow(element);
                int column = GetColumn(element);
                double elementRowOffset = Rows.GetOffset(row);
                double elementColumnOffset = Columns.GetOffset(column);
                double topRowOffset = Rows.GetOffset(Top);
                double leftColumnOffset = Columns.GetOffset(Left);
                //\\r = layoutInfo.GetRect(new CellAddress(WorksheetGrid.GetRow(element), WorksheetGrid.GetColumn(element)), new CellSpan(WorksheetGrid.GetRowSpan(element), WorksheetGrid.GetColumnSpan(element)));
                var pt1 = new Point(elementColumnOffset - leftColumnOffset,
                                    elementRowOffset - topRowOffset);
                /*r = new Rect(
                    new Point(Columns.GetOffset(WorksheetGrid.GetColumn(element)) - Columns.GetOffset(this.Left),
                        Rows.GetOffset(WorksheetGrid.GetRow(element)) - Rows.GetOffset(this.Top)),
                    new Point(Columns.GetOffset(WorksheetGrid.GetColumn(element) + WorksheetGrid.GetColumnSpan(element)) - Columns.GetOffset(Left),
                        Rows.GetOffset(WorksheetGrid.GetRow(element) + WorksheetGrid.GetRowSpan(element)) - Rows.GetOffset(Top)));
               */
                double w, h;
                if (GetIsFullRow(element))
                    w = size.Width;
                else
                {
                    Column column2 = Columns[GetColumn(element) + GetColumnSpan(element) - 1];
                    double test = column2.Offset + column2.ActualWidth;
                    //\\  test = Columns.GetOffset(GetColumn(element) + GetColumnSpan(element)-1);
                    w = test - leftColumnOffset - pt1.X;
                }

                if (GetIsFullColumn(element))
                    h = size.Height;
                else
                {
                    Row row2 = Rows[GetRow(element) + GetRowSpan(element) - 1];
                    h = row2.Offset + row2.ActualHeight - topRowOffset - pt1.Y;
                }
                r = new Rect(pt1, new Size(w, h));
                r = new Rect(r.Left + GetColumnOffset(element), r.Top + GetRowOffset(element), r.Width, r.Height);

                if (GetIsFullColumn(element))
                {
                    r = new Rect(r.Left, 0, r.Width, size.Height);
                }
                if (GetIsFullRow(element))
                {
                    r = new Rect(0, r.Top, size.Width, r.Height);
                }
                w = GetItemWidth(element);
                h = GetItemHeight(element);
               // if (w != 0 && h != 0)
               //     r = new Rect(r.Left, r.Top, w, h);
                if (!double.IsNaN(w) )
                    r = new Rect(r.Left, r.Top, w, r.Height);
                if (!double.IsNaN(h))
                    r = new Rect(r.Left, r.Top, r.Width, h);
                //\\ no need?element.Measure(r.Size);
            }
            if (GetVerticalOverflow(element))
                r = new Rect(r.Left, r.Top, r.Width, element.DesiredSize.Height);
            if (clipToBounds)
                r = new Rect(r.Left, r.Top, Math.Min(r.Width, size.Width + 20), Math.Min(r.Height, size.Height + 20));
            element.Arrange(r);
        }


        private Rect GetRect(CellAddress address, int colSpan, int rowSpan)
        {
            Rect r = layoutInfo.GetRect(address, new CellSpan(rowSpan, colSpan));
            return r;
        }

        private Rect GetRect(CellAddress address, int colSpan, int rowSpan, Size minSize)
        {
            double ColX = Columns.GetOffset(address.Column);
            double RowY = Rows.GetOffset(address.Row);
            int lastCol = Columns.GetIndexForOffset(ColX + minSize.Width) + 1;
            for (int lrow = address.Row; lrow < address.Row + rowSpan; lrow++)
            {
                List<int> l;
                if (positions.TryGetValue(lrow, out l))
                    //\\if (l != null)
                {
                    foreach (int i in l)
                    {
                        if (i > address.Column)
                        {
                            lastCol = Math.Min(lastCol, i);
                        }
                    }
                }
            }
            //   int lastRow = Rows.GetIndexForOffset(RowY + minSize.Height);
            double LastColX = Math.Max(Columns.GetOffset(address.Column + colSpan), Columns.GetOffset(lastCol));
            double LastRowY = Math.Max(Rows.GetOffset(address.Row + rowSpan), 0);
            return new Rect(ColX - Columns.GetOffset(Left), RowY - Rows.GetOffset(Top), LastColX - ColX, LastRowY - RowY);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            if (Columns == null || Rows == null)
                return new Size(0, 0);
            CalculatePositions();
            foreach (UIElement element in InternalChildren)
            {
                if (GetOverflow(element))
                    element.Measure(availableSize);
                else
                {
                    Rect r = GetRect(element, availableSize);
                    element.Measure(r.Size);
                }
            }
            double w;
            w = 0;
            double h;
            h = 0;
            IWorksheetFragment p = GetWorksheetFragment(this);
            if (p != null)
            {
                if (p.DesiredWidth != null)
                    w = p.DesiredWidth.Value;
                if (p.DesiredHeight != null)
                    h = p.DesiredHeight.Value;
            }
            return new Size(w, h);
        }


        internal static WorksheetPane GetWorksheetPane(UIElement worksheetGrid)
        {
            Contract.Ensures(Contract.Result<WorksheetPane>() != null);
            return SpreadsheetElement.GetWorksheetPane(worksheetGrid);
        }

        internal static IWorksheetFragment GetWorksheetFragment(UIElement worksheetGrid)
        {
            Contract.Ensures(Contract.Result<IWorksheetFragment>() != null);
            return SpreadsheetElement.GetWorksheetFragment(worksheetGrid);
        }


        internal void MakeVisible(CellAddress a)
        {
            WorksheetPane pane = GetWorksheetPane(this);
            pane.EnsureVisible(a);
        }

        internal CellAddress GetCellAddressFrom(Func<FrameworkElement, Point> fn)
        {
            WorksheetGrid g = this;//\\ GetHitGrid(fn);
            return g.GetCellAddressAt(fn(g));
        }
        enum PanePosition
        {
            Before,
            On,
            After,
        }
        internal CellAddress GetCellAddressAt(Point point)
        {
            WorksheetPane pane = GetWorksheetPane(this);
            Point pt = point;
            var worksheet = pane.Worksheet;
            PanePosition xPosition;
            if (point.X > pane.Width)
                xPosition = PanePosition.After;
            else if (point.X < 0)
                xPosition = PanePosition.Before;
            else
            {
                xPosition = PanePosition.On;
            }
            PanePosition yPosition;
            if (point.Y > pane.Height)
                yPosition = PanePosition.After;
            else if (point.Y < 0)
                yPosition = PanePosition.Before;
            else
            {
                yPosition = PanePosition.On;
            }
            switch (xPosition)
            {
                case PanePosition.After:
                    {
                        switch (yPosition)
                        {
                            case PanePosition.After:
                                switch (pane.PaneType)
                                {
                                    case PaneType.BottomRight:
                                        break;
                                    case PaneType.BottomLeft:
                                        if (worksheet.BottomRightPane != null)
                                        {
                                            pt += new Vector(-pane.Width, 0);
                                            pane = worksheet.BottomRightPane;
                                        }
                                        break;
                                    case PaneType.TopLeft:
                                        if (worksheet.BottomRightPane != null)
                                        {
                                            pt += new Vector(-pane.Width, -pane.Height);
                                            pane = worksheet.BottomRightPane;
                                        }
                                        else if (worksheet.TopRightPane != null)
                                        {
                                            pt += new Vector(-pane.Width, 0);
                                            pane = worksheet.TopRightPane;
                                        }
                                        else if (worksheet.BottomLeftPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomLeftPane;
                                        }
                                        break;
                                    case PaneType.TopRight:
                                        if (worksheet.BottomRightPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomRightPane;
                                        }
                                        break;
                                }
                                break;
                            case PanePosition.Before: // x after, y before
                                switch (pane.PaneType)
                                {
                                    case PaneType.TopLeft:
                                        {
                                            if (worksheet.TopRightPane != null)
                                            {
                                                pt += new Vector(-pane.Width, 0);
                                                pane = worksheet.TopRightPane;
                                            }
                                        }
                                        break;
                                    case PaneType.BottomLeft:
                                        if (worksheet.TopRightPane != null)
                                        {
                                            pane = worksheet.TopRightPane;
                                            pt += new Vector(-worksheet.TopLeftPane.Width, +worksheet.TopLeftPane.Height);
                                        }
                                        else
                                        {
                                            pt += new Vector(0, +pane.Height);
                                            pane = worksheet.TopLeftPane;
                                        }
                                        break;
                                    case PaneType.TopRight:
                                        break;
                                    case PaneType.BottomRight:
                                        break;
                                }
                                break;
                            case PanePosition.On:
                                switch (pane.PaneType)
                                {
                                    case PaneType.TopLeft:
                                        {
                                            if (worksheet.TopRightPane != null)
                                            {
                                                pt -= new Vector(pane.Width, 0);
                                                pane = worksheet.TopRightPane;
                                            }
                                        }
                                        break;
                                    case PaneType.BottomLeft:
                                        if (worksheet.BottomRightPane != null)
                                        {
                                            pt -= new Vector(pane.Width, 0);
                                            pane = worksheet.BottomRightPane;
                                        }
                                        break;
                                    case PaneType.TopRight:
                                    case PaneType.BottomRight:
                                        break;
                                }
                                break;
                        }
                    }
                    break;
                case PanePosition.Before:
                    {
                        switch (yPosition)
                        {
                            case PanePosition.After:
                                switch (pane.PaneType)
                                {
                                    case PaneType.BottomRight:
                                    case PaneType.BottomLeft:
                                        break;
                                    case PaneType.TopLeft:
                                        if (worksheet.BottomLeftPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomLeftPane;
                                        }
                                        break;
                                    case PaneType.TopRight:
                                        if (worksheet.BottomLeftPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomLeftPane;
                                            pt += new Vector(+pane.Width, 0);
                                        }
                                        else
                                        {
                                            pane = worksheet.TopLeftPane;
                                            pt += new Vector(+pane.Width, 0);
                                        }
                                        break;
                                }
                                break;
                            case PanePosition.Before: // x before, y before
                                switch (pane.PaneType)
                                {
                                    case PaneType.TopLeft:
                                        break;
                                    case PaneType.BottomLeft:
                                        pane = worksheet.TopLeftPane;
                                        pt += new Vector(0, pane.Height);
                                        break;
                                    case PaneType.TopRight:
                                        pane = worksheet.TopLeftPane;
                                        pt += new Vector(pane.Width, 0);
                                        break;
                                    case PaneType.BottomRight:
                                        pane = worksheet.TopLeftPane;
                                        pt += new Vector(pane.Width, pane.Height);
                                        break;
                                }
                                break;
                            case PanePosition.On: // x before, y on
                                switch (pane.PaneType)
                                {
                                    case PaneType.TopLeft:
                                        break;
                                    case PaneType.BottomLeft:
                                        break;
                                    case PaneType.TopRight:
                                        pane = worksheet.TopLeftPane;
                                        pt += new Vector(pane.Width, 0);
                                        break;
                                    case PaneType.BottomRight:
                                        pane = worksheet.BottomLeftPane;
                                        pt += new Vector(pane.Width, 0);
                                        break;
                                }
                                break;
                        }
                    }
                    break;
                case PanePosition.On:
                    {
                        switch (yPosition)
                        {
                            case PanePosition.After:
                                switch (pane.PaneType)
                                {
                                    case PaneType.BottomLeft:
                                    case PaneType.BottomRight:
                                        break;
                                    case PaneType.TopLeft:
                                        if (worksheet.BottomLeftPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomLeftPane;
                                        }
                                        break;
                                    case PaneType.TopRight:
                                        if (worksheet.BottomRightPane != null)
                                        {
                                            pt += new Vector(0, -pane.Height);
                                            pane = worksheet.BottomRightPane;
                                        }
                                        break;
                                }
                                break;
                            case PanePosition.Before:// x on, y before
                                switch (pane.PaneType)
                                {
                                    case PaneType.TopLeft:
                                        break;
                                    case PaneType.BottomLeft:
                                        pane = worksheet.TopLeftPane;
                                        pt += new Vector(0, pane.Height);
                                        break;
                                    case PaneType.TopRight:
                                        break;
                                    case PaneType.BottomRight:
                                        pane = worksheet.TopRightPane;
                                        pt += new Vector(0, pane.Height);
                                        break;
                                }
                                break;
                            case PanePosition.On:
                                break;
                        }
                    }
                    break;
            }
            return pane.GetCellAddressAt(pt);
            var cellAddress = pane.GetCellAddressAt(point);

            if (layoutInfo == null)
                return new CellAddress(0, 0); //\\ problem?
            return layoutInfo.GetCellAddressAt(point);
        }

        private static Worksheet GetWorksheet(object sender)
        {
            return WorksheetHelper.GetWorksheet(sender);
        }

        #region Nested type: LayoutInfo

        internal class LayoutInfo
        {
            private readonly List<double> _columnOffsets = new List<double>();
            private readonly List<double> _rowOffsets = new List<double>();
            private CellAddress _topLeft;
            private readonly ColumnCollection _columns;
            private readonly RowCollection _rows;

            internal LayoutInfo(CellPosition topLeft, ColumnCollection columns, RowCollection rows, Size size)
            {
                this._columns = columns;
                this._rows = rows;
                _topLeft = topLeft;
                double l = 0;
                int i = topLeft.Column;
                while (l < size.Width && columns.ValidIndex(i))
                {
                    _columnOffsets.Add(l);
                    l += columns[i].ActualWidth;
                    i++;
                }
                _columnOffsets.Add(l);

                l = 0;
                i = topLeft.Row;
                while (l < size.Height && rows.ValidIndex(i))
                {
                    _rowOffsets.Add(l);
                    l += rows[i].ActualHeight;
                    i++;
                }
                _rowOffsets.Add(l);
            }

            public LayoutInfo(WorksheetGrid grid, ColumnCollection columns, RowCollection rows, Size size)
                : this(new CellAddress(grid.Top, grid.Left), columns, rows, size)
            {
            }

            internal CellAddress GetCellAddressAt(Point point)
            {
                int i;
                int j;
                bool found = false;
                for (i = 0; i < _columnOffsets.Count; i++)
                {
                    if (_columnOffsets[i] > point.X)
                    {
                        i--;
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    i = this._columns.GetIndexForOffset(point.X) - _topLeft.Column;
                }
                found = false;
                for (j = 0; j < _rowOffsets.Count; j++)
                {
                    if (_rowOffsets[j] > point.Y)
                    {
                        j--;
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    j = this._rows.GetIndexForOffset(point.Y) - _topLeft.Row;
                }
                int rowIndex = j + _topLeft.Row;
                rowIndex = Math.Max(rowIndex, 0);
                rowIndex = Math.Min(rowIndex, Worksheet.MaxRowIndex);
                int columnIndex = i + _topLeft.Column;
                columnIndex = Math.Max(columnIndex, 0);
                columnIndex = Math.Min(columnIndex, Worksheet.MaxColumnIndex);
                return new CellAddress(rowIndex, columnIndex);
            }


            private double GetColumnOffset(int i)
            {
                i = i - _topLeft.Column;
                if (i < 0)
                    return 0;
                if (i >= _columnOffsets.Count)
                    return _columnOffsets[_columnOffsets.Count - 1];
                return _columnOffsets[i];
            }

            private double GetRowOffset(int i)
            {
                i = i - _topLeft.Row;
                if (i < 0)
                    return 0;
                if (i >= _rowOffsets.Count)
                    return _rowOffsets[_rowOffsets.Count - 1];
                return _rowOffsets[i];
            }


            public Rect GetRect(CellAddress address, CellSpan span)
            {
                double y1 = GetRowOffset(address.Row);
                double x1 = GetColumnOffset(address.Column);

                if (span.Row == 0)
                    span = new CellSpan(1, 1);
                if (span.Column == 0)
                    span = new CellSpan(1, 1); //\\ tofix
                double y2 = GetRowOffset(address.Row + span.Row);
                double x2 = GetColumnOffset(address.Column + span.Column);
                return new Rect(x1, y1, x2 - x1, y2 - y1);
            }
        }

        #endregion
    }
}